using Microsoft.Extensions.Logging;
using _3Dto2D.Interfaces;

namespace _3Dto2D.Services;

public class ProgressReporter<T> : IProgressReporter<T>
{
    private readonly ILogger<ProgressReporter<T>> _logger;

    public event EventHandler<ProgressEventArgs<T>>? ProgressChanged;

    public ProgressReporter(ILogger<ProgressReporter<T>> logger)
    {
        _logger = logger;
    }

    public async Task ProcessWithProgressAsync<TItem>(IEnumerable<TItem> items, Func<TItem, Task<T>> processor)
    {
        var itemList = items.ToList();
        var total = itemList.Count;
        var completed = 0;

        _logger.LogInformation("Starting processing of {Total} items", total);

        foreach (var item in itemList)
        {
            try
            {
                var result = await processor(item);
                completed++;

                var progressArgs = new ProgressEventArgs<T>
                {
                    Percentage = (completed * 100) / total,
                    CurrentItem = completed,
                    TotalItems = total,
                    Result = result,
                    Message = $"Processed item {completed} of {total}"
                };

                ProgressChanged?.Invoke(this, progressArgs);

                if (completed % 10 == 0 || completed == total)
                {
                    _logger.LogInformation("Progress: {Completed}/{Total} ({Percentage}%)", 
                        completed, total, progressArgs.Percentage);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to process item {Item}", item);
                
                var errorArgs = new ProgressEventArgs<T>
                {
                    Percentage = (completed * 100) / total,
                    CurrentItem = completed,
                    TotalItems = total,
                    Message = $"Error processing item: {ex.Message}"
                };

                ProgressChanged?.Invoke(this, errorArgs);
            }
        }

        _logger.LogInformation("Completed processing {Total} items", total);
    }
}

public class BatchProcessor<TInput, TOutput>
{
    private readonly ILogger<BatchProcessor<TInput, TOutput>> _logger;
    private readonly SemaphoreSlim _semaphore;

        public BatchProcessor(ILogger<BatchProcessor<TInput, TOutput>> logger, int maxConcurrency)
    {
        _logger = logger;
        _semaphore = new SemaphoreSlim(maxConcurrency, maxConcurrency);
    }

    public async Task<List<TOutput>> ProcessBatchAsync(
        IEnumerable<TInput> items,
        Func<TInput, Task<TOutput>> processor,
        IProgress<BatchProgressEventArgs>? progress = null,
        CancellationToken cancellationToken = default)
    {
        var itemList = items.ToList();
        var results = new List<TOutput>(itemList.Count);
        var completed = 0;
        var total = itemList.Count;

        _logger.LogInformation("Starting batch processing of {Total} items with max concurrency {MaxConcurrency}", 
            total, _semaphore.CurrentCount);

        var tasks = itemList.Select(async item =>
        {
            await _semaphore.WaitAsync(cancellationToken);
            try
            {
                var result = await processor(item);
                
                var currentCompleted = Interlocked.Increment(ref completed);
                
                progress?.Report(new BatchProgressEventArgs
                {
                    Completed = currentCompleted,
                    Total = total,
                    Percentage = (currentCompleted * 100) / total
                });

                return result;
            }
            finally
            {
                _semaphore.Release();
            }
        });

        var taskResults = await Task.WhenAll(tasks);
        results.AddRange(taskResults);

        _logger.LogInformation("Completed batch processing of {Total} items", total);
        return results;
    }
}

public class BatchProgressEventArgs
{
    public int Completed { get; init; }
    public int Total { get; init; }
    public int Percentage { get; init; }
    public string? Message { get; init; }
    public Exception? Error { get; init; }
}
